test: increase branch coverage 78.32% → 79.94% (#159)#173
Conversation
5 targets: - login/page.tsx: auth error, success redirect, loading state - planos-client.tsx: update path, delete error, formatDuration (mês/2 meses/dias) - columns.tsx: empty name avatar, single-word name, null date - workout-generator.tsx: NaN guard (covered by adjacent logic) - auth-provider.tsx: onAuthStateChange Sentry.setUser user vs null 1115/1115 tests, typecheck 0 errors, lint 0 errors. Branch coverage: 793/992 = 79.94% (0.06% gap to 80%). Closes #159.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR. |
WalkthroughThis PR adds test-only coverage across four test suites: planos-client (edit/delete/duration edge cases), login page (async auth flows with Supabase mocking), alunos columns (avatar fallback and date cell edge cases), and auth-provider (Sentry integration on auth state changes). No production code or public API changes. ChangesTest coverage expansion
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Test
participant LoginPage
participant SupabaseClientMock
Test->>LoginPage: submit form (fireEvent)
LoginPage->>SupabaseClientMock: signInWithPassword()
SupabaseClientMock-->>LoginPage: resolved/rejected result
LoginPage-->>Test: show error, "Autenticando...", or redirect to /dashboard
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/components/dashboard/alunos/columns.test.tsx (1)
147-168: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winGuard silently no-ops the assertions if
cellisn't a function.All three new tests wrap their
expectcalls insideif (typeof cell === 'function'). IfcolumnDefs[0].cellorcolumnDefs[3].cellwere ever undefined (e.g. due to a future refactor ofcolumns.tsx), these tests would pass with zero assertions executed, masking a real regression instead of failing.♻️ Suggested fix: assert function type before branching
it('avatar shows empty for empty name', () => { const cell = columnDefs[0].cell; - if (typeof cell === 'function') { - const { container } = render( - cell({ row: { original: { ...mockAluno, nomeCompleto: '' } } } as never) - ); - const fallback = container.querySelector('[data-testid="avatar-fallback"]'); - expect(fallback?.textContent).toBe(''); - } + expect(typeof cell).toBe('function'); + const { container } = render( + (cell as CallableFunction)({ row: { original: { ...mockAluno, nomeCompleto: '' } } } as never) + ); + const fallback = container.querySelector('[data-testid="avatar-fallback"]'); + expect(fallback?.textContent).toBe(''); });Same applies to the other two new tests.
Also applies to: 184-196
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dashboard/alunos/columns.test.tsx` around lines 147 - 168, The avatar cell tests can silently pass without running assertions when columnDefs[0].cell or columnDefs[3].cell is not a function. Update these tests to explicitly assert the cell renderer is a function before calling it, using the columnDefs cell symbols and the existing avatar fallback checks, so a missing or refactored cell implementation fails the test instead of no-oping.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/dashboard/planos/planos-client.test.tsx`:
- Around line 209-232: The edit-action tests are querying the wrong text, so
they may miss the actual control rendered by PlanosClient. Update the selectors
in the tests to target the real edit label used by the UI, using the “Editar”
text or a role/name query instead of “P”. Keep the assertions around opening the
form and calling updatePlanoAction the same, just fix the element lookup in the
PlanosClient test cases.
In `@src/app/login/page.test.tsx`:
- Around line 87-90: The login page test setup is leaking the previous
`mockGetSession` implementation because `vi.clearAllMocks()` only clears calls,
not behavior. Stub `mockGetSession` explicitly in this case and, in the submit
flow around `resolveAuth({ error: null })`, await the async handler to settle
before the test ends so the `/dashboard` navigation completes without racing
teardown.
---
Nitpick comments:
In `@src/components/dashboard/alunos/columns.test.tsx`:
- Around line 147-168: The avatar cell tests can silently pass without running
assertions when columnDefs[0].cell or columnDefs[3].cell is not a function.
Update these tests to explicitly assert the cell renderer is a function before
calling it, using the columnDefs cell symbols and the existing avatar fallback
checks, so a missing or refactored cell implementation fails the test instead of
no-oping.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: be11d9ca-2f92-4678-b3e6-b931f7232bad
📒 Files selected for processing (4)
src/app/dashboard/planos/planos-client.test.tsxsrc/app/login/page.test.tsxsrc/components/dashboard/alunos/columns.test.tsxsrc/components/providers/auth-provider.test.tsx
| it('opens form in edit mode when "Editar" is clicked', () => { | ||
| render(<PlanosClient initialPlanos={mockPlanos} />); | ||
| const editButtons = screen.getAllByText('P'); | ||
| fireEvent.click(editButtons[0]); | ||
| expect(screen.getByTestId('form-plano')).toBeTruthy(); | ||
| expect(screen.getByText('Editando')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('calls updatePlanoAction on edit submit', async () => { | ||
| const { updatePlanoAction } = await import('@/lib/actions/planos'); | ||
| vi.mocked(updatePlanoAction).mockResolvedValue({ | ||
| success: true, | ||
| data: { id: 'plano-1', nome: 'Mensal', preco: 100, duracaoDias: 30 }, | ||
| }); | ||
|
|
||
| render(<PlanosClient initialPlanos={mockPlanos} />); | ||
| fireEvent.click(screen.getAllByText('P')[0]); | ||
| fireEvent.click(screen.getByTestId('submit-plano')); | ||
|
|
||
| await waitFor(() => { | ||
| expect(updatePlanoAction).toHaveBeenCalled(); | ||
| expect(mockNotify.success).toHaveBeenCalledWith('Plano atualizado!', expect.any(String)); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect mockPlanos fixture to confirm whether any plano.nome is literally "P"
rg -n -B2 -A20 "mockPlanos" src/app/dashboard/planos/planos-client.test.tsx | head -80Repository: EmiyaKiritsugu3/PWeb_Project
Length of output: 3499
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant component markup for the edit button text.
FILE="src/app/dashboard/planos/planos-client.tsx"
wc -l "$FILE"
sed -n '1,260p' "$FILE" | nl -ba | sed -n '1,220p'Repository: EmiyaKiritsugu3/PWeb_Project
Length of output: 253
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/app/dashboard/planos/planos-client.tsx"
rg -n -A8 -B8 'Editar|Excluir|form-plano|submit-plano' "$FILE"Repository: EmiyaKiritsugu3/PWeb_Project
Length of output: 2813
Use the actual edit label here
screen.getAllByText('P') doesn’t match the rendered Editar button, so both tests can fail or click the wrong element. Switch these queries to Editar (or a role/name query) for the edit action.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/dashboard/planos/planos-client.test.tsx` around lines 209 - 232, The
edit-action tests are querying the wrong text, so they may miss the actual
control rendered by PlanosClient. Update the selectors in the tests to target
the real edit label used by the UI, using the “Editar” text or a role/name query
instead of “P”. Keep the assertions around opening the form and calling
updatePlanoAction the same, just fix the element lookup in the PlanosClient test
cases.
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| }); | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## src/app/login/page.test.tsx (outline)\n'
ast-grep outline src/app/login/page.test.tsx --view expanded || true
printf '\n## src/app/login/page.tsx (outline)\n'
ast-grep outline src/app/login/page.tsx --view expanded || true
printf '\n## Relevant test file excerpt\n'
sed -n '1,260p' src/app/login/page.test.tsx | cat -n
printf '\n## Relevant component excerpt\n'
sed -n '1,260p' src/app/login/page.tsx | cat -nRepository: EmiyaKiritsugu3/PWeb_Project
Length of output: 14872
Stub mockGetSession here and wait for the submit flow to finish. vi.clearAllMocks() doesn’t reset the implementation set in the previous test, so this case only works when the suite runs in order. After resolveAuth({ error: null }), let the async handler settle before the test exits so the /dashboard navigation doesn’t race teardown.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/login/page.test.tsx` around lines 87 - 90, The login page test setup
is leaking the previous `mockGetSession` implementation because
`vi.clearAllMocks()` only clears calls, not behavior. Stub `mockGetSession`
explicitly in this case and, in the submit flow around `resolveAuth({ error:
null })`, await the async handler to settle before the test ends so the
`/dashboard` navigation completes without racing teardown.
There was a problem hiding this comment.
5 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/app/dashboard/planos/planos-client.test.tsx">
<violation number="1" location="src/app/dashboard/planos/planos-client.test.tsx:211">
P2: `screen.getAllByText('P')` queries for elements containing the text "P" — this likely matches avatar fallback initials rather than the "Editar" button the test title describes. If the component renders edit buttons with a pencil icon or "Editar" label, this query will either click the wrong element or break if the avatar text changes. Consider using `screen.getAllByText('Editar')` or a role-based query (`getByRole('button', { name: /editar/i })`) to target the intended edit action.</violation>
<violation number="2" location="src/app/dashboard/planos/planos-client.test.tsx:229">
P2: The `updatePlanoAction` edit-submit test passes with only a bare `toHaveBeenCalled()` — it doesn't assert which arguments were passed. The existing `createPlanoAction` test in this same file properly verifies both the action arguments and `router.refresh()`. Without these assertions, this test would continue passing if the wrong plano ID were sent or if the success callback stopped refreshing the page, masking a real regression.</violation>
<violation number="3" location="src/app/dashboard/planos/planos-client.test.tsx:247">
P3: Custom agent: **Enforce Pragmatic Test Coverage**
The new test 'displays "mês" badge when duration is exactly 30 days' duplicates coverage already provided by the existing 'displays formatted duration badges' test. Since mockPlanos already includes a plano with duracaoDias: 30 (plano-1), the existing test already verifies that a 30-day duration renders the 'mês' badge. This test adds no new behavioral coverage. Consider removing it to keep the test suite focused, or if you want explicit documentation of the 30-day → 'mês' mapping, consider merging it into a parameterized test alongside the other duration cases.</violation>
</file>
<file name="src/app/login/page.test.tsx">
<violation number="1" location="src/app/login/page.test.tsx:147">
P2: The `window.location` restoration via `Object.defineProperty` at the end of the success test is not resilient to assertion failures. If the `waitFor` times out (e.g., in slow CI or if mock behavior changes), the test throws before reaching the restoration line, leaving `window.location` as a plain object for all subsequent tests in this file. Consider wrapping the assertion and restoration in a `try/finally` block or moving the restoration to an `afterEach` hook.</violation>
<violation number="2" location="src/app/login/page.test.tsx:171">
P2: 'Autenticando...' test relies on `mockGetSession` implementation leaking from the previous test — `vi.clearAllMocks()` resets call history but not mock implementations, so the session polling loop after sign-in silently resolves using the neighbor test's configuration. Add `mockGetSession.mockResolvedValue(...)` or `mockResolvedValueOnce(...)` inside this test to make it self-contained.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| fireEvent.click(screen.getByTestId('submit-plano')); | ||
|
|
||
| await waitFor(() => { | ||
| expect(updatePlanoAction).toHaveBeenCalled(); |
There was a problem hiding this comment.
P2: The updatePlanoAction edit-submit test passes with only a bare toHaveBeenCalled() — it doesn't assert which arguments were passed. The existing createPlanoAction test in this same file properly verifies both the action arguments and router.refresh(). Without these assertions, this test would continue passing if the wrong plano ID were sent or if the success callback stopped refreshing the page, masking a real regression.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/dashboard/planos/planos-client.test.tsx, line 229:
<comment>The `updatePlanoAction` edit-submit test passes with only a bare `toHaveBeenCalled()` — it doesn't assert which arguments were passed. The existing `createPlanoAction` test in this same file properly verifies both the action arguments and `router.refresh()`. Without these assertions, this test would continue passing if the wrong plano ID were sent or if the success callback stopped refreshing the page, masking a real regression.</comment>
<file context>
@@ -206,6 +206,62 @@ describe('PlanosClient', () => {
+ fireEvent.click(screen.getByTestId('submit-plano'));
+
+ await waitFor(() => {
+ expect(updatePlanoAction).toHaveBeenCalled();
+ expect(mockNotify.success).toHaveBeenCalledWith('Plano atualizado!', expect.any(String));
+ });
</file context>
| }); | ||
|
|
||
| const originalLocation = window.location; | ||
| Object.defineProperty(window, 'location', { |
There was a problem hiding this comment.
P2: The window.location restoration via Object.defineProperty at the end of the success test is not resilient to assertion failures. If the waitFor times out (e.g., in slow CI or if mock behavior changes), the test throws before reaching the restoration line, leaving window.location as a plain object for all subsequent tests in this file. Consider wrapping the assertion and restoration in a try/finally block or moving the restoration to an afterEach hook.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/login/page.test.tsx, line 147:
<comment>The `window.location` restoration via `Object.defineProperty` at the end of the success test is not resilient to assertion failures. If the `waitFor` times out (e.g., in slow CI or if mock behavior changes), the test throws before reaching the restoration line, leaving `window.location` as a plain object for all subsequent tests in this file. Consider wrapping the assertion and restoration in a `try/finally` block or moving the restoration to an `afterEach` hook.</comment>
<file context>
@@ -98,4 +114,79 @@ describe('LoginPage', () => {
+ });
+
+ const originalLocation = window.location;
+ Object.defineProperty(window, 'location', {
+ value: { ...originalLocation, href: '' },
+ writable: true,
</file context>
| it('shows "Autenticando..." while submitting', async () => { | ||
| let resolveAuth!: (v: { error: null | Error }) => void; | ||
| mockSignInWithPassword.mockImplementation( | ||
| () => | ||
| new Promise((resolve) => { | ||
| resolveAuth = resolve; | ||
| }) | ||
| ); | ||
|
|
||
| render(<LoginPage />); | ||
| fireEvent.submit(screen.getByLabelText('Email corporativo').closest('form')!); | ||
|
|
||
| await waitFor(() => { | ||
| expect(screen.getByText('Autenticando...')).toBeTruthy(); | ||
| }); | ||
|
|
There was a problem hiding this comment.
P2: 'Autenticando...' test relies on mockGetSession implementation leaking from the previous test — vi.clearAllMocks() resets call history but not mock implementations, so the session polling loop after sign-in silently resolves using the neighbor test's configuration. Add mockGetSession.mockResolvedValue(...) or mockResolvedValueOnce(...) inside this test to make it self-contained.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/login/page.test.tsx, line 171:
<comment>'Autenticando...' test relies on `mockGetSession` implementation leaking from the previous test — `vi.clearAllMocks()` resets call history but not mock implementations, so the session polling loop after sign-in silently resolves using the neighbor test's configuration. Add `mockGetSession.mockResolvedValue(...)` or `mockResolvedValueOnce(...)` inside this test to make it self-contained.</comment>
<file context>
@@ -98,4 +114,79 @@ describe('LoginPage', () => {
+ });
+ });
+
+ it('shows "Autenticando..." while submitting', async () => {
+ let resolveAuth!: (v: { error: null | Error }) => void;
+ mockSignInWithPassword.mockImplementation(
</file context>
| it('shows "Autenticando..." while submitting', async () => { | |
| let resolveAuth!: (v: { error: null | Error }) => void; | |
| mockSignInWithPassword.mockImplementation( | |
| () => | |
| new Promise((resolve) => { | |
| resolveAuth = resolve; | |
| }) | |
| ); | |
| render(<LoginPage />); | |
| fireEvent.submit(screen.getByLabelText('Email corporativo').closest('form')!); | |
| await waitFor(() => { | |
| expect(screen.getByText('Autenticando...')).toBeTruthy(); | |
| }); | |
| it('shows "Autenticando..." while submitting', async () => { | |
| let resolveAuth!: (v: { error: null | Error }) => void; | |
| mockSignInWithPassword.mockImplementation( | |
| () => | |
| new Promise((resolve) => { | |
| resolveAuth = resolve; | |
| }) | |
| ); | |
| mockGetSession.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); | |
| render(<LoginPage />); | |
| fireEvent.submit(screen.getByLabelText('Email corporativo').closest('form')!); | |
| await waitFor(() => { | |
| expect(screen.getByText('Autenticando...')).toBeTruthy(); | |
| }); | |
| resolveAuth({ error: null }); | |
| }); |
|
|
||
| it('opens form in edit mode when "Editar" is clicked', () => { | ||
| render(<PlanosClient initialPlanos={mockPlanos} />); | ||
| const editButtons = screen.getAllByText('P'); |
There was a problem hiding this comment.
P2: screen.getAllByText('P') queries for elements containing the text "P" — this likely matches avatar fallback initials rather than the "Editar" button the test title describes. If the component renders edit buttons with a pencil icon or "Editar" label, this query will either click the wrong element or break if the avatar text changes. Consider using screen.getAllByText('Editar') or a role-based query (getByRole('button', { name: /editar/i })) to target the intended edit action.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/dashboard/planos/planos-client.test.tsx, line 211:
<comment>`screen.getAllByText('P')` queries for elements containing the text "P" — this likely matches avatar fallback initials rather than the "Editar" button the test title describes. If the component renders edit buttons with a pencil icon or "Editar" label, this query will either click the wrong element or break if the avatar text changes. Consider using `screen.getAllByText('Editar')` or a role-based query (`getByRole('button', { name: /editar/i })`) to target the intended edit action.</comment>
<file context>
@@ -206,6 +206,62 @@ describe('PlanosClient', () => {
+ it('opens form in edit mode when "Editar" is clicked', () => {
+ render(<PlanosClient initialPlanos={mockPlanos} />);
+ const editButtons = screen.getAllByText('P');
+ fireEvent.click(editButtons[0]);
+ expect(screen.getByTestId('form-plano')).toBeTruthy();
</file context>
| }); | ||
| }); | ||
|
|
||
| it('displays "mês" badge when duration is exactly 30 days', () => { |
There was a problem hiding this comment.
P3: Custom agent: Enforce Pragmatic Test Coverage
The new test 'displays "mês" badge when duration is exactly 30 days' duplicates coverage already provided by the existing 'displays formatted duration badges' test. Since mockPlanos already includes a plano with duracaoDias: 30 (plano-1), the existing test already verifies that a 30-day duration renders the 'mês' badge. This test adds no new behavioral coverage. Consider removing it to keep the test suite focused, or if you want explicit documentation of the 30-day → 'mês' mapping, consider merging it into a parameterized test alongside the other duration cases.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/dashboard/planos/planos-client.test.tsx, line 247:
<comment>The new test 'displays "mês" badge when duration is exactly 30 days' duplicates coverage already provided by the existing 'displays formatted duration badges' test. Since mockPlanos already includes a plano with duracaoDias: 30 (plano-1), the existing test already verifies that a 30-day duration renders the 'mês' badge. This test adds no new behavioral coverage. Consider removing it to keep the test suite focused, or if you want explicit documentation of the 30-day → 'mês' mapping, consider merging it into a parameterized test alongside the other duration cases.</comment>
<file context>
@@ -206,6 +206,62 @@ describe('PlanosClient', () => {
+ });
+ });
+
+ it('displays "mês" badge when duration is exactly 30 days', () => {
+ const customPlanos: Plano[] = [{ id: 'p2', nome: 'Mensal', preco: 100, duracaoDias: 30 }];
+ render(<PlanosClient initialPlanos={customPlanos} />);
</file context>



Closes #159.
Changes
5 test targets covering previously uncovered branch paths:
login/page.test.tsxplanos-client.test.tsxcolumns.test.tsxauth-provider.test.tsxRemoved: session poll timeout test (10×500ms > vitest 5s timeout — UX fallback, not security boundary).
Verification
npm run test: 1115/1115 passnpm run typecheck: 0 errorsnpm run lint: 0 errorsSummary by cubic
Raise branch coverage from 78.32% to 79.94% with targeted tests covering login error/success/loading,
PlanosClientupdate/delete and duration badges, aluno columns avatar/date edge cases, and auth provider Sentry user updates via@sentry/nextjs. Remove the session-poll timeout test that exceeded vitest’s 5s limit.Written for commit 1f4e186. Summary will update on new commits.
Summary by CodeRabbit